package com.local.goods.service.impl;

import com.alibaba.fastjson.JSON;
import com.local.common.constants.BusinessException;
import com.local.common.constants.ExceptionCodeEnum;
import com.local.common.util.PageResultUtil;
import com.local.common.util.ResultUtil;
import com.local.goods.constants.GoodsConstants;
import com.local.goods.mapper.GoodsMapper;
import com.local.goods.pojo.Goods;
import com.local.goods.pojo.GoodsESDoc;
import com.local.goods.pojo.GoodsESRequestParam;
import com.local.goods.service.GoodsService;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.local.goods.constants.GoodsConstants.GOODS_ES_MAPPING_TEMPLATE;

@Service
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private RestHighLevelClient esClient;

    /**
     * 查询商品列表，支持翻页和搜索
     * @param page
     * @param limit
     * @param sort
     * @param order
     * @param goodsName
     * @param goodsSellStatus
     * @return
     */
    @Override
    public PageResultUtil<Goods> findGoodsList(Integer page, Integer limit, String sort, String order, String goodsName, Integer goodsSellStatus) {
        Integer start = (page - 1) * limit;
        List<Goods> goodsList = goodsMapper.findGoodsList(start, limit, sort, order, goodsName, goodsSellStatus);
        Integer totalGoods = goodsMapper.getTotalGoods(goodsName, goodsSellStatus);
        PageResultUtil<Goods> goodsPageResultUtil = new PageResultUtil<>(goodsList, totalGoods, limit, page);
        return goodsPageResultUtil;
    }

    /**
     * 新增
     * @param goods
     * @return
     */
    @Override
    public Integer save(Goods goods) {
        return goodsMapper.save(goods);
    }

    /**
     * 通过id查询
     * @param goodsId
     * @return
     */
    @Override
    public Goods getById(Integer goodsId) {
        return goodsMapper.getById(goodsId);
    }

    /**
     * 修改
     * @param goods
     * @return
     */
    @Override
    public Integer update(Goods goods) {
        return goodsMapper.update(goods);
    }

    /**
     * 更新状态，上架和下架
     * @param ids
     * @param goodsSellStatus
     * @param updateUser
     * @param updateTime
     * @return
     */
    @Override
    public Integer updateStatus(Integer[] ids, Integer goodsSellStatus, Integer updateUser, String updateTime) {
        return goodsMapper.updateStatus(ids, goodsSellStatus, updateUser, updateTime);
    }


    /**
     * 通过category id查询商品列表
     * @param categoryId
     * @param page
     * @param limit
     * @param sort
     * @param order
     * @param goodsSellStatus
     * @return
     */
    @Override
    public PageResultUtil<Goods> findGoodsListByCategoryId(Integer categoryId, Integer page, Integer limit, String sort, String order, Integer goodsSellStatus) {
        Integer start = (page - 1) * limit;
        List<Goods> goodsListByCategoryId = goodsMapper.findGoodsListByCategoryId(categoryId, start, limit, sort, order, 0);
        Integer totalByCategoryId = goodsMapper.getTotalByCategoryId(categoryId, 0);
        return new PageResultUtil<>(goodsListByCategoryId, totalByCategoryId, limit, page);
    }

    /**
     * 通过id查询商品，不包括“商品详情”字段
     * @param id
     * @return
     */
    @Override
    public Goods getSimpleById(Integer id) {
        return goodsMapper.getByGoodsId(id);
    }

    /**
     * 通过ids批量查询商品列表
     * @param goodsIds
     * @return
     */
    @Override
    public List<Goods> getGoodsListByIds(List<Integer> goodsIds) {
        return goodsMapper.getGoodsListByIds(goodsIds);
    }


    /**
     * 批量更新商品的库存（当购物车生成订单的时候）
     * @param goodsStockNumList
     * @return
     */
    public ResultUtil updateGoodsStockNum(List<Goods> goodsStockNumList) {
        Integer result = goodsMapper.updateGoodsStockNum(goodsStockNumList);
        if (Objects.nonNull(result) && result > 0) {
            return new ResultUtil(200, "修改库存成功", "");
        } else {
            return new ResultUtil(201, "修改库存失败，请稍后再试", "");
        }
    }


    /**
     * 初始化es中商品goods文档的数据，将mysql中goods表的数据，同步到es中goods文档
     * @return
     */
    @Override
    public Integer initSearchDocData(Integer page) {

        //文档总数
        Integer totalCount = 0;

        try {
            //判断goods索引是否存在
            GetIndexRequest request = new GetIndexRequest("goods");
            boolean exists = this.esClient.indices().exists(request, RequestOptions.DEFAULT);

            //如果不存在goods索引，则创建
            if (!exists) {
                //1.创建request请求
                CreateIndexRequest indexRequest = new CreateIndexRequest("goods");

                //2.准备请求所需参数：DSL语句
                indexRequest.source(GOODS_ES_MAPPING_TEMPLATE, XContentType.JSON);

                //3.发送请求
                this.esClient.indices().create(indexRequest, RequestOptions.DEFAULT);
            }

            //从mysql查询goods数据，同步到goods文档
            Integer limit = 50;
            Integer start = (page - 1) * 0;
            start = (page - 1) * limit;
            List<Goods> goodsList = goodsMapper.findGoodsList(start, limit, "goods_id", "desc", null, 0);
            if (goodsList.isEmpty()) {
                return 0;
            }

            //商品数累加
            totalCount += goodsList.size();

            //1.创建批量添加的请求
            BulkRequest bulkRequest = new BulkRequest();

            for (Goods goods : goodsList) {
                GoodsESDoc goodsESDoc = new GoodsESDoc(goods);

                //2.拼接数据
                bulkRequest.add(new IndexRequest("goods")
                        .id(goodsESDoc.getGoodsId().toString())
                        .source(JSON.toJSONString(goodsESDoc), XContentType.JSON));
            }

            //3.发起请求
            this.esClient.bulk(bulkRequest, RequestOptions.DEFAULT);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        return totalCount;
    }


    /**
     * 通过关键词keywords对goods进行es搜索
     * @param param
     * @return
     */
    @Override
    public PageResultUtil<GoodsESDoc> search(GoodsESRequestParam param) {

        try {
            //1.创建请求
            SearchRequest searchRequest = new SearchRequest("goods");

            //2.构建参数和查询条件
            buildBasicQuery(param, searchRequest);

            //2.1 分页
            Integer page = param.getPage();
            Integer size = param.getSize();
            page = Objects.isNull(page) || page <= 0 ? 1 : page;
            size = Objects.isNull(size) || size <= 0 ? 10 : size;
            searchRequest.source().from((page - 1) * size).size(size);
            //2.2 排序，默认按es的_score desc排序，即es默认的算分规则降序
            String sortBy = param.getSortBy();
            String order = param.getOrder();
            sortBy = Objects.isNull(sortBy) || sortBy.isEmpty() ? "_score" : sortBy;
            order = Objects.isNull(order) || order.isEmpty() ? "desc" : order;
            searchRequest.source().sort(sortBy, SortOrder.fromString(order));

            //2.3搜索词高亮显示
            searchRequest.source().highlighter(new HighlightBuilder().field("goodsName").requireFieldMatch(false).field("goodsIntro").requireFieldMatch(false));

            //3.发起请求
            SearchResponse searchResponse = this.esClient.search(searchRequest, RequestOptions.DEFAULT);

            //4.返回结果
            return handleResponse(searchResponse);
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 构建参数和查询条件
     * @param param
     * @param searchRequest
     */
    private static void buildBasicQuery(GoodsESRequestParam param, SearchRequest searchRequest) {
        //2.构建参数和查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //搜索关键词
        String keywords = param.getKeywords();
        if (Objects.isNull(keywords) || keywords.isEmpty()) {
            //如果关键词为空，则查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", keywords));
        }

        //价格
        Integer minPrice = param.getMinPrice();
        Integer maxPrice = param.getMaxPrice();
        if (!(Objects.nonNull(minPrice) && minPrice > 0 && Objects.nonNull(maxPrice) && maxPrice > 0 && minPrice > maxPrice)) {
            if (Objects.nonNull(minPrice) && minPrice > 0) {
                boolQuery.filter(QueryBuilders.rangeQuery("sellingPrice").gte(minPrice));
            }
            if (Objects.nonNull(maxPrice) && maxPrice > 0) {
                boolQuery.filter(QueryBuilders.rangeQuery("sellingPrice").lte(maxPrice));
            }
        }

            /*
            //算分控制，人为干预排名
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                    boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    QueryBuilders.termQuery("isAD", true),
                                    ScoreFunctionBuilders.weightFactorFunction(10)
                            )
                    }
            );
             */

        searchRequest.source().query(boolQuery);
    }


    /**
     * 返回搜索结果
     * @param searchResponse
     * @return
     */
    private PageResultUtil<GoodsESDoc> handleResponse(SearchResponse searchResponse) {
        //解析结果
        SearchHits hits = searchResponse.getHits();
        //4.1 得到结果总数
        TotalHits totalHits = hits.getTotalHits();

        SearchHit[] hitsArr = hits.getHits();
        List<GoodsESDoc> list = new ArrayList<>();
        for (SearchHit documentFields : hitsArr) {
            //得到结果中的source
            String source = documentFields.getSourceAsString();
            //将source转化成Object对象
            GoodsESDoc goodsESDoc = JSON.parseObject(source, GoodsESDoc.class);

            //得到结果中的hignlight高亮字段
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                HighlightField goodsNameHighlightField = highlightFields.get("goodsName");
                if (Objects.nonNull(goodsNameHighlightField)) {
                    String goodsName = goodsNameHighlightField.getFragments()[0].string();
                    goodsESDoc.setGoodsName(goodsName);
                }

                HighlightField goodsIntroHighlightField = highlightFields.get("goodsIntro");
                if (Objects.nonNull(goodsIntroHighlightField)) {
                    String goodsIntro = goodsIntroHighlightField.getFragments()[0].string();
                    goodsESDoc.setGoodsIntro(goodsIntro);
                }
            }

            list.add(goodsESDoc);
        }

        return new PageResultUtil<GoodsESDoc>(list, (int) totalHits.value, 0, 0);
    }


    /**
     * 搜索的自动补齐，即搜索建议
     * @param keywords
     * @return
     */
    public List<String> searchSuggestion(String keywords) {
        try {
            //1.创建请求
            SearchRequest request = new SearchRequest("goods");

            //2.准备DSL：搜索的自动补全
            request.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestions", //搜索名称，后面解析结果的时候要用到，根据名称来获取结果
                    SuggestBuilders.completionSuggestion("suggestion") //自动补全的字段
                            .prefix(keywords) //搜索词
                            .skipDuplicates(true) //过滤重复的数据
                            .size(10) //返回结果数
            ));

            //3.发起请求
            SearchResponse response = this.esClient.search(request, RequestOptions.DEFAULT);

            //4.结果解析
            Suggest suggest = response.getSuggest();
            //4.1根据查询的名称，获取结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
            //获取options
            List<CompletionSuggestion.Entry.Option> suggestionsOptions = suggestions.getOptions();

            //循环options，获取自动补全的内容
            List<String> suggestionList = new ArrayList<>();
            for (CompletionSuggestion.Entry.Option suggestionsOption : suggestionsOptions) {
                String suggestionText = suggestionsOption.getText().toString();
                suggestionList.add(suggestionText);
            }

            return suggestionList;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 新增或更新goods的es文档：如果文档不存在，则新增；如果文档存在，则更新
     * @param idString: 1 || 1,2,3
     */
    @Override
    public void updateSearchDocData(String idString) {
        try {
            if (idString.length() > 0) {
                /*
                List<Integer> goodsIdList = Arrays.stream(idString.split(",")).map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) {
                        return Integer.valueOf(s);
                    }
                }).collect(Collectors.toList());
                 */
                List<Integer> goodsIdList = Arrays.stream(idString.split(",")).map(s -> Integer.valueOf(s)).collect(Collectors.toList());
                List<Goods> goodsList = goodsMapper.getGoodsListByIds(goodsIdList);
                for (Goods goods : goodsList) {
                    //0.构建文档数据
                    GoodsESDoc goodsESDoc = new GoodsESDoc(goods);

                    //1.创建请求
                    IndexRequest request = new IndexRequest("goods").id(goodsESDoc.getGoodsId().toString());

                    //2.准备json文档
                    request.source(JSON.toJSONString(goodsESDoc), XContentType.JSON);

                    //3.发起新增请求：如果该文档不存在，则新增；如果存在，则会更新
                    this.esClient.index(request, RequestOptions.DEFAULT);
                }
            }
        } catch (IOException e) {
            throw new BusinessException(ExceptionCodeEnum.BUSINESS_EXCEPTION.getCode(), ExceptionCodeEnum.BUSINESS_EXCEPTION.getMessage());
        }
    }


    /**
     * 删除goods的es文档
     * @param idString idString: 1 || 1,2,3
     */
    @Override
    public void deleteSearchDocData(String idString) {
        try {
            if (idString.length() > 0) {
                List<Integer> goodsIdList = Arrays.stream(idString.split(",")).map(s -> Integer.valueOf(s)).collect(Collectors.toList());
                List<Goods> goodsList = goodsMapper.getGoodsListByIds(goodsIdList);
                for (Goods goods : goodsList) {
                    //创建请求
                    DeleteRequest request = new DeleteRequest("goods", goods.getGoodsId().toString());

                    //发起删除的请求
                    this.esClient.delete(request, RequestOptions.DEFAULT);
                }
            }
        } catch (IOException e) {
            throw new BusinessException(ExceptionCodeEnum.BUSINESS_EXCEPTION.getCode(), ExceptionCodeEnum.BUSINESS_EXCEPTION.getMessage());
        }
    }
}
